/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.tencent.tendon.convert.json.listeners;

import com.tencent.tendon.convert.json.JsonListener;
import com.tencent.tendon.convert.json.JsonReader;
import com.tencent.tendon.convert.json.JsonWriter;

/**
 *
 * @author nbzhang
 */
abstract class JsonAbstractListener<T> extends JsonListener<T> {

    private static final char[] CHARS_TUREVALUE = "true".toCharArray();

    private static final char[] CHARS_FALSEVALUE = "false".toCharArray();

    private static final char[] CHARS_INT_MINVALUE = "-2147483648".toCharArray();

    private static final char[] CHARS_LONG_MINVALUE = "-9223372036854775808".toCharArray();

    public final void convertBoolTo(JsonWriter out, boolean value) {
        out.write(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
    }

    public final boolean convertBoolFrom(final JsonReader in) {
        char[] chars = in.readValue();
        if (chars.length == 1 && chars[0] == '1') return true;
        if (chars.length != 4) return false;
        if (chars[0] == 't' && chars[1] == 'r' && chars[2] == 'u' && chars[3] == 'e') return true;
        if (chars[0] == 'T' && chars[1] == 'R' && chars[2] == 'U' && chars[3] == 'E') return true;
        return false;
    }

    public final void convertByteTo(JsonWriter out, byte value) {
        this.convertIntTo0(out, (int) value);
    }

    public final byte convertByteFrom(final JsonReader in) {
        return (byte) in.readInt();
    }

    public final void convertShortTo(JsonWriter out, short value) {
        this.convertIntTo0(out, (int) value);
    }

    public final short convertShortFrom(final JsonReader in) {
        return (short) in.readInt();
    }

    public final void convertCharTo(JsonWriter out, char value) {
        this.convertIntTo0(out, (int) value);

    }

    public final char convertCharFrom(final JsonReader in) {
        return (char) in.readInt();
    }

    public final void convertIntTo(JsonWriter out, int value) {
        this.convertIntTo0(out, value);
    }

    private void convertIntTo0(JsonWriter out, int value) {
        if (value == Integer.MIN_VALUE) {
            out.write(CHARS_INT_MINVALUE);
            return;
        }
        int i = value;

        int size = intStringSize(i < 0 ? -i : i);
        char[] buf = out.expand(size + (i < 0 ? 1 : 0));
        final boolean indirect = out.indirect();
        int count = indirect ? 0 : out.count();
        if (i < 0) {
            buf[count++] = '-';
            i = -i;
        }
        //-------------来自JDK源码 Integer.getChars(int i, int index, char[] buf)------------------
        int q, r;
        int charPos = count + size;
        // Generate two digits per iteration
        while (i >= 65536) {
            q = i / 100;
            // really: r = i - (q * 100);
            r = i - ((q << 6) + (q << 5) + (q << 2));
            i = q;
            buf[--charPos] = DigitOnes[r];
            buf[--charPos] = DigitTens[r];
        }
        char[] digits0 = digits;
        for (;;) {
            q = (i * 52429) >>> (16 + 3);
            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
            buf[--charPos] = digits0[r];
            i = q;
            if (i == 0) break;
        }
        //-------------------------------------------------------------------------------------------
        out.count(count + size);
        if (indirect) out.write(buf);
    }

    public int convertIntFrom(final JsonReader in) {
        return in.readInt();
    }

    public void convertFloatTo(final JsonWriter out, final float value) {
        out.write(false, String.valueOf(value));
    }

    public float convertFloatFrom(final JsonReader in) {
        char[] chars = in.readValue();
        if (chars == null) return 0.f;
        return Float.parseFloat(new String(chars));
    }

    public void convertLongTo(JsonWriter out, long value) {
        if (value == Long.MIN_VALUE) {
            out.write(CHARS_LONG_MINVALUE);
            return;
        }
        long i = value;

        int size = longStringSize(i < 0 ? -i : i);
        char[] buf = out.expand(size + (i < 0 ? 1 : 0));
        final boolean indirect = out.indirect();
        int count = indirect ? 0 : out.count();
        if (i < 0) {
            buf[count++] = '-';
            i = -i;
        }
        //-------------来自JDK源码 Long.getChars(int i, int index, char[] buf)------------------
        long q;
        int r;
        int charPos = count + size;
        // Get 2 digits/iteration using longs until quotient fits into an int
        while (i > Integer.MAX_VALUE) {
            q = i / 100;
            // really: r = i - (q * 100);
            r = (int) (i - ((q << 6) + (q << 5) + (q << 2)));
            i = q;
            buf[--charPos] = DigitOnes[r];
            buf[--charPos] = DigitTens[r];
        }

        // Get 2 digits/iteration using ints
        int q2;
        int i2 = (int) i;
        while (i2 >= 65536) {
            q2 = i2 / 100;
            // really: r = i2 - (q * 100);
            r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
            i2 = q2;
            buf[--charPos] = DigitOnes[r];
            buf[--charPos] = DigitTens[r];
        }
        // Fall thru to fast mode for smaller numbers
        // assert(i2 <= 65536, i2);
        for (;;) {
            q2 = (i2 * 52429) >>> (16 + 3);
            r = i2 - ((q2 << 3) + (q2 << 1));  // r = i2-(q2*10) ...
            buf[--charPos] = digits[r];
            i2 = q2;
            if (i2 == 0) break;
        }
        //-------------------------------------------------------------------------------------------
        out.count(count + size);
        if (indirect) out.write(buf);
    }

    public long convertLongFrom(final JsonReader in) {
        return in.readLong();
    }

    public void convertDoubleTo(final JsonWriter out, final double value) {
        out.write(false, String.valueOf(value));
    }

    public double convertDoubleFrom(final JsonReader in) {
        char[] chars = in.readValue();
        if (chars == null) return 0.0;
        return Double.parseDouble(new String(chars));
    }

    public void convertStringTo(final JsonWriter out, final String value) {
        if (value == null) {
            out.writeNull();
            return;
        }
        char[] content = out.expand(value.length() * 2 + 2);
        final boolean indirect = out.indirect();
        int count = indirect ? 0 : out.count();
        content[count++] = '"';
        for (char ch : value.toCharArray()) {
            if (ch == '"' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\\') content[count++] = '\\';
            content[count++] = ch;
        }
        content[count++] = '"';
        out.count(count);
        if (indirect) out.write(content, 0, count);
    }

    public String convertStringFrom(final JsonReader in) {
        return in.readString();
    }

//------------------------------------------------------------------------------------------------------
    private static int intStringSize(final int x) {
        for (int i = 0;; i++) {
            if (x <= intSizeTable[i]) return i + 1;
        }
    }

    private static int longStringSize(final long x) {
        long p = 10;
        for (int i = 1; i < 19; i++) {
            if (x < p) return i;
            p = 10 * p;
        }
        return 19;
    }

    private static final int[] intSizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE};

    private static final char[] digits = {
        '0', '1', '2', '3', '4', '5',
        '6', '7', '8', '9', 'a', 'b',
        'c', 'd', 'e', 'f', 'g', 'h',
        'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't',
        'u', 'v', 'w', 'x', 'y', 'z'
    };

    private static final char[] DigitTens = {
        '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
        '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
        '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
        '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
        '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
        '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
        '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
        '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
        '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
        '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',};

    private static final char[] DigitOnes = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',};
}
